/*
 * ProcesamientoArgumentos.c
 *
 *  Created on: 27/03/2012
 *      Author: putaro
 */
#include "ProcesamientoArgumentos.h"
#include "Ordenamiento.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/*
 * Pre: El parámetro vector es no nulo.
 * Post: Se imprimen todos los caracteres del vector por salida estándar.
 */
void imprimirVectorPorSalidaEstandar(unsigned char* vector ,
						const unsigned long int const tamanio );

/*
 * Pre: -
 * Post: Si la ruta del archivo es correcta se carga el contenido del mismo
 * en el vector y se retorna cualquier número distinto de cero.
 * Si la ruta del archivo es inválida o nula y el vector esta vacío se
 * pedirá el ingreso de datos por teclado y se retorna cualquier número
 * distinto de cero.
 * En caso que el vector no este vacío y el archivo sea incorrecto o
 * nulo se retorna cero manteniendose el contenido previo del vector.
 *
 */
int procesarCargaDatos(const char* ruta , unsigned char** vector ,
						unsigned long int* const tamanio);

/*
 * Pre: Los parámetros son no nulos.
 * Post: Si coincide con alguna de las opciones correctas se modifica opcionSalida a
 * la misma y se retorna cualquier valor distinto de cero, caso contrario 0.
 */
int procesarOpciones(const char* opcionEntrada , char* const opcionSalida);

/*
 * Pre: Los parámetros son no nulos.
 * Post: Se han cargado datos desde el teclado en el vector parámetro.
 */
void cargarDatosDesdeStdin(unsigned char** vector ,
							unsigned long int* const tamanio);

/*
 * Pre: Los parámetros son no nulos.
 * Post: Se han cargado datos desde el archivo dado por el parámetro ruta.
 */
unsigned int cargarDatosDesdeArchivo(const char* ruta , unsigned char** vector ,
						unsigned long int* const tamanio);

/*
 * Pre: Los parámetros han sido correctamente inicializados.
 * Post: Se ejecuto la opcion en base al estado de los parámetros.
 */
int ejecutarOpcion(unsigned char** vector ,	unsigned long int* const tamanio ,
					const char opcion);

/*
 * Pre: "argc" es el numero de elementos que contiene "argv".
 * "argv" es un vector con un conjunto de argumentos de entrada los cuales
 * pueden nombres de archivos u opciones dadas por el siguiente formato:
 * -h, --help
 * -V, --version
 * -m, --merge
 * -s, --sel
 * El primer parámetro es la opción, luego pueden ser las rutas de uno o
 * varios archivos en cualquier orden.
 * En caso que el parámetro vector sea un puntero nulo se creará el mismo.
 * Si el parámetro vector no es nulo se agregaran los datos nuevos al final
 * del mismo, siempre y cuando alcance la capacidad del vector.
 *
 * Post: Se han procesando los argumentos inicializando el vector parámetro
 * y su longitud.
 * NOTA: La destrucción del vector queda a cargo del usuario.
 */
void procesarArgumentos(const int argc , char* const argv[] ,
		unsigned char** vector , unsigned long int* const tamanio) {
	char opcion;
	*vector = NULL;
	*tamanio = 0;
	opcion = SELECTIONSORT; /* por defecto selection sort*/
	if(argc > 1) {
		if(argc == 2) {
			if(!procesarOpciones(argv[1] , &opcion)) {
				if ((opcion != HELP) && (opcion != VERSION))
					procesarCargaDatos(argv[1] , vector , tamanio);
			} else {
				if((opcion != HELP) && (opcion != VERSION)) {
					procesarCargaDatos(NULL , vector , tamanio);
				}
			}
		} else {
			unsigned int i;
			if(procesarOpciones(argv[1] , &opcion)) {
				i = 2;
			} else {
				i = 1;
			}
			if((opcion != HELP) && (opcion != VERSION)) {
				while(i < argc) {
					procesarCargaDatos(argv[i] , vector ,
									tamanio);
					i++;
				}
			}
		}
	} else {
		/* Si no hay opciones se cargan datos y se ordena por el método
		 * Selection Sort*/
		procesarCargaDatos(NULL , vector , tamanio);
	}
	ejecutarOpcion(vector, tamanio , opcion);
}

/*
 * Pre: Los parámetros son no nulos.
 * Post: Si coincide con alguna de las opciones correctas se modifica opcionSalida a
 * la misma y se retorna cualquier valor distinto de cero, caso contrario 0.
 */
int procesarOpciones(const char* opcionEntrada , char* const opcionSalida) {
	int retorno = 0;
	if (opcionEntrada != NULL) {
		if((strlen(opcionEntrada) == 2)) {
			if(opcionEntrada[0] == '-') {
				switch (opcionEntrada[1]) {
					case HELP :	{
						*opcionSalida = HELP;
						retorno = 1;
						break;
					}
					case VERSION : {
						*opcionSalida = VERSION;
						retorno = 1;
						break;
					}
					case MERGESORT : {
						*opcionSalida = MERGESORT;
						retorno = 1;
						break;
					}
					case SELECTIONSORT : {
						*opcionSalida = SELECTIONSORT;
						retorno = 1;
						break;
					}
					case MERGESORTINVERSO : {
						*opcionSalida = MERGESORTINVERSO;
						retorno = 1;
						break;
					}
					default : {
						fprintf(stderr,"ERROR: Opción inválida.\n");
					}
				}
			}
		}
	}
	return retorno;
}

/*
 * Pre: -
 * Post: Si la ruta del archivo es correcta se carga el contenido del mismo
 * en el vector y se retorna cualquier número distinto de cero.
 * Si la ruta del archivo es inválida o nula y el vector esta vacío se
 * pedirá el ingreso de datos por teclado y se retorna cualquier número
 * distinto de cero.
 * En caso que el vector no este vacío y el archivo sea incorrecto o
 * nulo se retorna cero manteniendose el contenido previo del vector.
 *
 */
int procesarCargaDatos(const char* ruta , unsigned char** const vector ,
						unsigned long int* const tamanio) {
	int retorno = 0;
	if(ruta != NULL) {
		if(strlen(ruta) > 0) {
			/* carga desde archivo*/
			retorno = cargarDatosDesdeArchivo(ruta , vector , tamanio);
		} else {
			/* carga desde stdin*/
			cargarDatosDesdeStdin(vector , tamanio);
			retorno = 1;
		}
	} else {
		if(*tamanio == 0) {
			/* carga desde stdin*/
			cargarDatosDesdeStdin(vector , tamanio);
			retorno = 1;
		}
	}
	return retorno;
}

/*
 * Pre: Los parámetros son no nulos.
 * Post: Se han cargado datos desde el teclado en el vector parámetro.
 */
void cargarDatosDesdeStdin(unsigned char** const vector ,
							unsigned long int* const tamanio) {
	int fin;
	unsigned char c;
	*vector =  malloc(sizeof(unsigned char) * TAMANIO_MAXIMO_VECTOR);
	memset(*vector , 0 , sizeof(unsigned char) * TAMANIO_MAXIMO_VECTOR);
	*tamanio = 0;
	fin = 0;
	c = '\n';
	while((!fin) && ((*tamanio) < TAMANIO_MAXIMO_VECTOR )) {
		int cantidad = 0;
		cantidad = scanf("%c" , &c);
		fin = (cantidad < 1) || (c == '\n');
		if(!fin) {
			(*vector)[*tamanio] = c;
			(*tamanio)++;
		}
	}
}

/*
 * Pre: Los parámetros son no nulos.
 * Post: Se han cargado datos desde el archivo dado por el parámetro ruta.
 */
unsigned int cargarDatosDesdeArchivo(const char* ruta , unsigned char** vector ,
						unsigned long int* const tamanio) {
	int fin , retorno = 0;
	unsigned char c;
	FILE* file;
	file = fopen(ruta , "r");
	if(*vector == NULL) {
		*vector = malloc(sizeof(unsigned char) * TAMANIO_MAXIMO_VECTOR);
		memset(*vector , 0 , sizeof(unsigned char) * TAMANIO_MAXIMO_VECTOR);
		*tamanio = 0;
	}
	fin = (file == NULL);
	c = 0;
	while((!fin) && ((*tamanio) < TAMANIO_MAXIMO_VECTOR )) {
		int cantidad = 0;
		cantidad = fread(&c , sizeof(unsigned char) , 1 , file);
		if (cantidad > 0) {
			fin = feof(file);
			if(!fin) {
				(*vector)[*tamanio] = c;
				(*tamanio)++;
				retorno = 1;
			}
		} else {
			fin = 1;
		}
	}
	if(file != NULL) {
		fclose(file);
	} else {
		fprintf(stderr , "ERROR: No se encontro el archivo.\n");
	}
	return retorno;
}

/*
 * Pre: Los parámetros han sido correctamente inicializados.
 * Post: Se ejecuto la opcion en base al estado de los parámetros.
 */
int ejecutarOpcion(unsigned char** vector ,	unsigned long int* const tamanio ,
					const char opcion) {
	int retorno = 0;
	switch (opcion) {
		case HELP : {
			printf("tp0 [OPTIONS][file...]\n");
			printf("-h, --help			display	this help and exit.\n");
			printf("-v, --version		display version informaticon and exit.\n");
			printf("-m, --merge			use the mergesort algorithm.\n");
			printf("-s, --selection		use the selection sort algorithm. \n");
			retorno = 1;
			break;
		}
		case VERSION : {
			printf("%s\n",VERSION_PROGRAMA);
			retorno = 1;
			break;
		}
		case MERGESORT : {
			mergeSort(*vector , *tamanio);
			retorno = 1;
			imprimirVectorPorSalidaEstandar(*vector , *tamanio);
			break;
		}
		case MERGESORTINVERSO : {
			mergeSortInverso(*vector , *tamanio);
			retorno = 1;
			imprimirVectorPorSalidaEstandar(*vector , *tamanio);
			break;
		}
		case SELECTIONSORT : {
			selectionSort(*vector , *tamanio);
			retorno = 1;
			imprimirVectorPorSalidaEstandar(*vector , *tamanio);
			break;
		}
	}
	return retorno;
}

/*
 * Pre: El parámetro vector es no nulo.
 * Post: Se imprimen todos los caracteres del vector por salida estándar.
 */
void imprimirVectorPorSalidaEstandar(unsigned char* vector ,
						const unsigned long int tamanio ) {
	unsigned long int i;
	for(i = 0 ; i < tamanio ; i++) {
		putchar(vector[i]);
	}
}
